package cn.cellcom.agent.online.client;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.cellcom.agent.biz.TGroupBiz;
import cn.cellcom.agent.biz.TSessionBiz;
import cn.cellcom.agent.common.AgentConstant;
import cn.cellcom.agent.common.AgentConstant.AGENT_JOIN_TYPE;
import cn.cellcom.agent.common.AgentConstant.AGENT_STATUS;
import cn.cellcom.agent.common.AgentConstant.AG_SESSION_END_TYPE;
import cn.cellcom.agent.common.AgentConstant.MEMBER_STATUS;
import cn.cellcom.agent.entity.TSettingEntity;
import cn.cellcom.agent.online.handler.LogHandler;
import cn.cellcom.agent.online.message.AgentConfigMessage;
import cn.cellcom.agent.online.message.ArchiveMessage;
import cn.cellcom.agent.online.message.MessageConstant;
import cn.cellcom.agent.online.message.MessageConstant.ARCHIVE_EVENT;
import cn.cellcom.agent.online.message.MessageConstant.MESSAGE_EVENT;
import cn.cellcom.agent.online.message.MessageConstant.MESSAGE_NOTIFY;
import cn.cellcom.agent.online.message.MessageConstant.MULIT_TYPE;
import cn.cellcom.agent.online.wrapper.GroupWrapper;
import cn.cellcom.agent.online.wrapper.SessionWrapper;
import cn.cellcom.agent.pojo.TAgentSession;
import cn.cellcom.agent.pojo.TChannel;
import cn.cellcom.agent.pojo.TSession;
import cn.cellcom.agent.pojo.TUser;
import cn.cellcom.agent.util.RedisManager;
import cn.cellcom.agent.util.SerializableUtils;
import cn.cellcom.jar.util.DT;
import cn.cellcom.jar.util.LogUtil;
import cn.cellcom.jar.util.MyException;

public class AgentClient extends Client {
	private Logger log = LoggerFactory.getLogger(this.getClass());

	private TSessionBiz sbiz;

	private TGroupBiz gbiz;

	private String httpSessionId;

	private TSettingEntity setting;

	private TUser agent;

	private java.util.Map<String, GroupWrapper> groups = new ConcurrentHashMap<String, GroupWrapper>();

	/**
	 * crmid--sessionid
	 */
	private java.util.Map<String, String> cid2sid = new ConcurrentHashMap<String, String>();

	/**
	 * 坐席的各种状态数据
	 */
	private AgentConfig agentConfig;

	/**
	 * 最后活跃时间
	 */
	private long lastActiveTime;

	/**
	 * 对象创建时间
	 */
	private long createTime = System.currentTimeMillis();

	public long getCreateTime() {
		return createTime;
	}

	public void setLastActiveTime() {
		this.lastActiveTime = System.currentTimeMillis();
	}

	/**
	 * 内存对象的活跃时间低于1分钟，则认为活跃的
	 * 
	 * @return
	 */
	public boolean isActive() {
		return System.currentTimeMillis() - lastActiveTime < DT.AMINUTE;
	}

	public AgentClient(TSessionBiz ssbiz, TGroupBiz gbiz, TUser user, String httpSessionId) {
		this.sbiz = ssbiz;
		this.gbiz = gbiz;

		this.agent = user;
		this.httpSessionId = httpSessionId;

		agentConfig = new AgentConfig();
	}

	/**
	 * 坐席上线：<br>
	 * 1、通知其他坐席<br>
	 */
	public void online() {
		log.info("[{}] call online method.", this.getId());
		notifyAgentInfo(true);
	}

	/**
	 * 给所有在线坐席发送一个消息
	 * 
	 * @param logon
	 *            如果是第一次上线，那么给自己发送其他坐席的状态
	 */
	public void notifyAgentInfo(boolean logon) {
		Set<AgentClient> agents = ClientManager.getInstance().getOnlineAgents(agent.getPid());
		log.info("[{}] 's status notify to other online agents[{}]", this.getId(), agents.size());
		if (agents != null) {
			Iterator<AgentClient> it = agents.iterator();
			while (it.hasNext()) {
				// 自己登陆的状态通知到其他坐席
				AgentClient otherAgent = it.next();
				AgentConfigMessage config = new AgentConfigMessage(this);
				config.setReceiver(otherAgent.getId());
				this.sendBizMessage(config);

				if (logon) {
					// 把别人的状态通知自己
					AgentConfigMessage config2 = new AgentConfigMessage(otherAgent);
					config2.setReceiver(this.getId());
					otherAgent.sendBizMessage(config2);
				}
			}
		}
	}

	/**
	 * 坐席下线
	 */
	public void logout() {
		// 清空缓存
		ClientManager.getInstance().remove(this.getId());
		log.info("[{}] logout, now to notify other agent and left all session", this.getId());
		// 设置为离线状态，然后状态通知
		this.getAgentConfig().setStatus(AGENT_STATUS.OFFLINE);
		notifyAgentInfo(false);

		for (String sid : getIdMap().values()) {
			log.info("[{}] logout, now to left the session[{}]", this.getId(), sid);
			if (setting.getAgentLogout() == AgentConstant.AGENT_LOGOU_POLICY.END_SESSION.ordinal()) {
				this.archive(this.getSession(sid), ARCHIVE_EVENT.BY_AG_LOGOUT, null);
				this.endSession(sid, AG_SESSION_END_TYPE.BY_AG_LOGOUT);
			} else {
				this.getSession(sid).left(this, MEMBER_STATUS.OFFLINE);
			}
		}
		leftGroup();
	}

	/**
	 * 
	 * @throws MyException
	 */
	public void leftGroup() {
		for (GroupWrapper g : groups.values()) {
			g.removeAgentClient(this);
		}
	}

	/**
	 * 坐席签入技能组,加载到内存,并通知其他坐席
	 * 
	 * @throws MyException
	 */
	public void joinGroup(List<String> gs) throws MyException {
		for (int i = 0; i < gs.size(); i++) {
			String id = gs.get(i);
			GroupWrapper g = gbiz.loadGroupToMemory(id);
			g.addAgentClient(this);
			this.groups.put(id, g);
		}
		/*
		 * Set<AgentClient> agents =
		 * ClientManager.getInstance().getOnlineAgents(agent.getOrg()); if (agents !=
		 * null) { Iterator<AgentClient> it = agents.iterator(); while (it.hasNext()) {
		 * AgentClient receiver = it.next(); //通知其他人 AgentJoinGroupMessage joing = new
		 * AgentJoinGroupMessage(agent, gs); joing.setReceiver(receiver.getId());
		 * super.systemSendBizMessage(joing);
		 * 
		 * //通知自己 AgentJoinGroupMessage join2 = new
		 * AgentJoinGroupMessage(receiver.getAgent(), gs);
		 * joing.setReceiver(agent.getId()); super.systemSendBizMessage(join2); } }
		 */

		List<SessionWrapper> queueList = QueueManager.getInstance().getQueue(getPid()).getIndexQueue();

		log.info("[{}] join group, now to notify the queue, size is [{}]", this.getId(), queueList.size());
		for (SessionWrapper session : queueList) {
			if (this.getGroups().get(session.getGroup().getId()) != null) {
				log.info("[{}] join group, now to notify the queue[{}] of group[{}]", this.getId(), session.getId(), session.getGroup().getId());
				this.notifyQueueAdded(session);
			}
		}
	}

	/**
	 * 接入会话
	 * 
	 * @param session
	 */
	public boolean receiveSession(SessionWrapper session) {
		log.info("[{}] recieive the session[{}]", this.getId(), session.getId());
		try {
			if (session.getAgsessionSize() > 0) {
				log.error("[{}] recieive the session[{}], but the agent is not null", this.getId(), session.getId());
				return false;
			}
			// 访客会话被接入,需要做相关数据处理和通知
			session.getVisitor().queueReceived(session, this.agent);

			// 该crm已经存在会话
			String crmId = session.getVisitor().getId();
			String existSid = this.getIdMap().get(crmId);
			if (existSid != null) {
				log.info("[{}] recieive the new session[{}] of crmId, end the exist session[{}] of the crm", this.getId(), session.getId(), existSid);
				endSession(existSid, AG_SESSION_END_TYPE.BY_REPEAT);
				archive(session, MessageConstant.ARCHIVE_EVENT.RE_VISIT, null);
			}

			// 绑定数据并写入数据库
			TAgentSession agsession = bindSession(session);
			// 告诉会话本人已经进入
			this.notifyReceivingSession(session);
			// 进入会话,会通知相关人(访客)
			session.joinAgsession(agsession, this, true);
			// session.join(this, true);

			// 通知所有坐席排队已经被接收
			for (AgentClient agent : session.getGroup().getAgents().values()) {
				agent.notifyQueueReceived(session);
			}

			// 记录最后的坐席
			RedisManager.getInstance().getLastAgent().putAndExpireString(session.getVisitor().getId() + "--" + session.getPid(), this.getId(),
					3600 * 24 * 30);
			return true;
		} catch (MyException e) {
			LogUtil.e(this.getClass(), "", e);
			return false;
		}
	}

	/**
	 * 接入会话
	 * 
	 * @param session
	 */
	public boolean receiveInvite(SessionWrapper session) {
		log.info("[{}] recieive the session[{}]", this.getId(), session.getId());
		try {
			// 该crm已经存在会话
			String crmId = session.getCrm().getId();
			String existSid = this.getIdMap().get(crmId);
			if (existSid != null) {
				log.info("[{}] recieive the new session[{}] of crmId, end the exist session[{}] of the crm", this.getId(), session.getId(), existSid);
				endSession(existSid, AG_SESSION_END_TYPE.BY_REPEAT);
				archive(session, MessageConstant.ARCHIVE_EVENT.RE_VISIT, null);
			}

			// 绑定数据并写入数据库
			TAgentSession agsession = bindSession(session);
			// 告诉会话本人已经进入
			this.notifyReceivingSession(session);
			// 进入会话,会通知相关人(访客)
			session.joinAgsession(agsession, this, true);
			// ////////////////////session.join(this, true);
			return true;
		} catch (MyException e) {
			LogUtil.e(this.getClass(), "", e);
			return false;
		}
	}

	/**
	 * 坐席结束会话
	 * 
	 * @param crmId
	 * @param sid
	 * @param by_repeat
	 */
	public void endSession(String sid, AG_SESSION_END_TYPE type) {
		SessionWrapper session = getSession(sid);
		if (session == null || session.getAgsession(this.getId()) == null) {
			log.error("[{}] end the session[{}], but the agentsession is null", this.getId(), sid);
			return;
		}
		/**
		 * 1、修改session内存<br>
		 * 2、存储<br>
		 * 3、通知会话<br>
		 * 4、通知状态<br>
		 * 5、其他
		 */
		TAgentSession agsession = session.getAgsession(this.getId()).getAgsession();
		// 通知自己会话已经结束，坐席收到这个包要处理
		if (session.getSystem() != null) {
			if (AG_SESSION_END_TYPE.AG_TRANSFER2_SUCC.equals(type)) {
				session.getSystem().sysSendNotify(this.getId(), MESSAGE_NOTIFY.AG_TRANSFER2_SUCCESS, session.getSession());
			} else {
				session.getSystem().sysSendNotify(this.getId(), MESSAGE_NOTIFY.AG_END_SESSION_ED, session.getSession());
			}
		}
		session.left(this, MEMBER_STATUS.END);

		// 转出会话
		if (AG_SESSION_END_TYPE.AG_TRANSFER2_SUCC.equals(type)) {
			log.info("[{}] transfer the session[{}] to other, so it is end.", this.getId(), session.getId());
		} else {
			// 主持人结束会话才需要操作访客
			if (session.getMastAgent() == null || session.getMastAgent().getId().equals(this.getId())) {
				if (session.getVisitor() != null) {// 访客在线则由访客自行调用结束会话
					log.info("[{}] end the session[{}] by type[{}], now to end the client session", this.getId(), session.getId(), type);
					session.getVisitor().endSession(session.getId(), AgentConstant.SESSION_END_TYPE.BY_AGENT);
				} else {// 如果访客不在线了则需要更新数据库
					log.info("[{}] end the session[{}], but visitor not online, so update the db.", this.getId(), session.getId());
					TSession tsession = session.getSession();
					if (StringUtils.isBlank(tsession.getEndType())) {
						tsession.setEndTime(System.currentTimeMillis());
						tsession.setEndType(type.name());
						this.updateSessionDb(tsession);
					}
				}
			} else {// 非主持人
				session.notifyToAgentForLelfSession();
			}
		}

		// 放在最后更新数据库，因为可能在left的时候系统还有发送消息需要计算
		agsession.setEndTime(System.currentTimeMillis());
		agsession.setEndType(type.name());
		this.updateSessionDb(agsession);
	}

	/**
	 * 调用数据库更新会话
	 */
	private void updateSessionDb(TAgentSession session) {
		try {
			sbiz.getDao().myUpdate(session);
		} catch (MyException e) {
			LogUtil.e(this.getClass(), "[" + session.getId() + "]update tagsession error", e);
		}
	}

	public void updateSessionDb(TSession session) {
		try {
			sbiz.getDao().myUpdate(session);
		} catch (MyException e) {
			LogUtil.e(this.getClass(), "[" + session.getId() + "]update tsession error", e);
		}
	}

	/**
	 * 
	 * @param session
	 * @return
	 * @throws MyException
	 */
	public TAgentSession bindSession(SessionWrapper session) throws MyException {
		AGENT_JOIN_TYPE joinType = null;
		if (session.getEvent() == null) {
			joinType = AgentConstant.AGENT_JOIN_TYPE.RECEIVE_QUEUE;
		} else if (session.getEvent().equals(MESSAGE_EVENT.AG_INVITE_AGENT)) {// 邀请
			joinType = AgentConstant.AGENT_JOIN_TYPE.AGENT_INVITE;
		} else if (session.getEvent().equals(MESSAGE_EVENT.AG_TRANSFER2_AGENT)) {// 转接
			joinType = AgentConstant.AGENT_JOIN_TYPE.AGENT_TRANSFER;
		}
		TAgentSession agsession = new TAgentSession();
		agsession.setMessageCount(0);
		agsession.setAgent(agent.getUsername());
		agsession.setAgentNickname(StringUtils.isBlank(agent.getNickName()) ? agent.getName() : agent.getNickName());
		agsession.setChannel(session.getSession().getChannel());
		agsession.setGroupId(session.getGroup().getId());
		agsession.setId(IDManager.getAgSid());
		agsession.setJoinTime(System.currentTimeMillis());
		agsession.setJoinType(joinType.name());
		agsession.setOrg(agent.getOrg());
		agsession.setPid(agent.getPid());
		agsession.setSession(session.getId());

		gbiz.getDao().mySave(agsession);
		log.info("[{}] bind agentsession[{}] to session[{}]", this.getId(), agsession.getId(), session.getId());
		return agsession;
	}

	/**
	 * 将坐席接入的会话详情发送给自己
	 * 
	 * @param session
	 */
	public void notifyReceivingSession(SessionWrapper session) {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("group", session.getGroup().getGroup());
		map.put("crm", session.getCrm());
		map.put("channel", session.getSession().getChannel());
		map.put("session", session.getSession());
		session.getSystem().sysSendNotify(this.getId(), MESSAGE_NOTIFY.INVITE, map);
	}

	/**
	 * 给该坐席发送一个会话邀请
	 * 
	 * @param session
	 * @param user
	 */
	public void notifySessionInvite(SessionWrapper session) {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("group", session.getGroup().getGroup());
		map.put("crm", session.getCrm());
		map.put("channel", session.getSession().getChannel());
		map.put("session", session.getSession());
		map.put("agent", this.getAgent());
		map.put("type", MESSAGE_NOTIFY.AG_INVITE_AGENT.name());
		session.getSystem().sysSendNotify(this.getId(), MESSAGE_NOTIFY.AG_INVITE_AGENT, map);
	}

	/**
	 * 给改坐席发送一个会话转接的通知
	 * 
	 * @param session
	 */
	public void notifySessionTransfer(SessionWrapper session) {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("group", session.getGroup().getGroup());
		map.put("crm", session.getCrm());
		map.put("channel", session.getSession().getChannel());
		map.put("session", session.getSession());
		map.put("agent", this.getAgent());
		map.put("type", MESSAGE_NOTIFY.AG_TRANSFER2_AGENT.name());
		session.getSystem().sysSendNotify(this.getId(), MESSAGE_NOTIFY.AG_TRANSFER2_AGENT, map);
	}

	/**
	 * 告知坐席某个邀请已经被拒接了
	 * 
	 * @param session
	 * @param user
	 */
	public void notifySessionInviteRejected(SessionWrapper session, AgentClient agent) {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("crm", session.getCrm());
		map.put("agent", agent.getAgent());
		session.getSystem().sysSendNotify(this.getId(), MESSAGE_NOTIFY.AG_INVITE_AGENT_REJECT_ED, map);
	}

	/**
	 * 告知坐席某个邀请已经被接入
	 * 
	 * @param session
	 * @param agent
	 */
	public void notifySessionInviteAccepted(SessionWrapper session, AgentClient agent) {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("crm", session.getCrm());
		map.put("agent", agent.getAgent());
		session.getSystem().sysSendNotify(this.getId(), MESSAGE_NOTIFY.AG_INVITE_AGENT_ACCEPT_ED, map);
	}

	/**
	 * 等待协助
	 */
	private Set<SessionWrapper> waitingSessions = new HashSet<SessionWrapper>();

	public Set<SessionWrapper> getWaitingSessions() {
		return waitingSessions;
	}

	/**
	 * 通知有用户进入排队
	 * 
	 * @param session
	 */
	public void notifyQueueAdded(SessionWrapper session) {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("group", session.getGroup().getGroup());
		map.put("crm", session.getVisitor().getCrm());
		map.put("channel", session.getVisitor().getChannel());
		session.getSystem().sysSendNotify(this.getId(), MESSAGE_NOTIFY.QUEUE_ADD_ED, map);
	}

	public void notifyQueueRemoved(SessionWrapper session) {
		session.getSystem().sysSendNotify(this.getId(), MESSAGE_NOTIFY.QUEUE_CANCEL_ED, session.getVisitor().getCrm());
	}

	public void notifyQueueReceived(SessionWrapper session) {
		session.getSystem().sysSendNotify(this.getId(), MESSAGE_NOTIFY.QUEUE_RECEIVE_ED, session.getVisitor().getCrm());
	}

	/**
	 * 接收的数量
	 * 
	 * @return
	 */
	public int getCurrentChat() {
		return getIdMap().size();
	}

	public TSettingEntity getSetting() {
		return setting;
	}

	public AgentConfig getAgentConfig() {
		return agentConfig;
	}

	@Override
	public String getId() {
		return agent.getUsername();
	}

	@Override
	public String getHttpSessionId() {
		return httpSessionId;
	}

	public TUser getAgent() {
		return agent;
	}

	public java.util.Map<String, GroupWrapper> getGroups() {
		return groups;
	}

	public void setSetting(TSettingEntity setting) {
		this.setting = setting;
		// 如果坐席设置了状态，然后去了其他页面，重新进入，则不需要改变状态。
		if (this.getAgentConfig().getStatus() == null) {
			// 设置了默认为离开状态
			this.getAgentConfig().setStatus(AGENT_STATUS.valueOf(setting.getLoginDefaultStatus()));
		}
		this.getAgentConfig().setMaxChat(setting.getChatMaxLimit());
	}

	public void archive(SessionWrapper session, ARCHIVE_EVENT event, String text) {
		this.archiveWithId(session, IDManager.getMid(), event.toString(), text, MULIT_TYPE.TEXT.name(), null);
	}

	public void archiveWithId(SessionWrapper session, String mid, String event, String text, String mtype, Object obj) {
		if (session == null) {
			return;
		}
		TSession tsession = session.getSession();
		ArchiveMessage am = new ArchiveMessage();
		try {
			// am.setCode();
			am.setEvent(event);
			am.setId(mid);
			if (obj != null) {
				am.setObject(new String(SerializableUtils.serialize(obj)));
			}
			am.setOrg(tsession.getOrg());
			am.setPid(tsession.getPid());
			am.setSender(this.getId());
			am.setSenderNickname(this.getNickname());
			if (event.equals(ARCHIVE_EVENT.AG_END_SESSION.name()) || event.equals(ARCHIVE_EVENT.RE_VISIT.name())
					|| event.equals(ARCHIVE_EVENT.AG_ACCEPT_INVITE.name()) || event.equals(ARCHIVE_EVENT.AG_REJECT_INVITE.name())
					|| event.equals(ARCHIVE_EVENT.AG_INVITE_AGENT.name()) || event.equals(ARCHIVE_EVENT.AG_END_INVITE.name())
					|| event.equals(ARCHIVE_EVENT.AG_TRANSFER2_AGENT.name())) {
				am.setShowPolicy(MessageConstant.ARCHIVE_SHOW_POLICY.MANAGER_AND_AGENT.name());
			} else {
				am.setShowPolicy(MessageConstant.ARCHIVE_SHOW_POLICY.ALL.name());
			}
			am.setSenderRole(AgentConstant.USER_TYPE_AGENT);
			am.setText(text);
			am.setSid(tsession.getId());
			am.setCid(tsession.getCrm());
			am.setTime(DT.getNow2());
			am.setMultiType(mtype);
			LogHandler.archive(am);
		} catch (Exception e) {
			log.error("Archive agent message fail:" + am, e);
		}
	}

	@Override
	public Map<String, String> getIdMap() {
		return cid2sid;
	}

	@Override
	public String getOrg() {
		return agent.getOrg();
	}

	public String getPid() {
		return agent.getPid();
	}

	@Override
	public String getNickname() {
		return agent.getNickName();
	}

	@Override
	public String getSender(TChannel tChannel) {
		if (tChannel != null && AgentConstant.CHANNEL_TYPE.WECHAT.name().equals(tChannel.getType())) {
			return agent.getNo() + "@" + tChannel.getWcAppid();
		} else {
			return agent.getUsername();
		}
	}

	public void setAgent(TUser user) {
		this.agent = user;
	}

	/**
	 * 
	 * @return
	 */
	public SessionWrapper getSession(String sid) {
		if (StringUtils.isBlank(sid)) {
			return null;
		}
		return SessionManager.getInstance().getPool(getPid()).getSession(sid);
	}
}
